Skip to content

Sandbox-aware Project Settings page#4632

Merged
elias-ba merged 4 commits intomainfrom
sandbox-settings-page
Apr 23, 2026
Merged

Sandbox-aware Project Settings page#4632
elias-ba merged 4 commits intomainfrom
sandbox-settings-page

Conversation

@elias-ba
Copy link
Copy Markdown
Contributor

@elias-ba elias-ba commented Apr 17, 2026

Description

This implements the per-page approach we agreed on in #3398. The Project Settings page now behaves differently when you're inside a sandbox: each tab tells you whether your changes will sync on merge, and the UI for some tabs changes shape to match what makes sense for a sandbox (no Delete button, MFA read-only, webhook security managed at the parent level, etc.).

The categorization, agreed in the issue thread:

Tab Category What it means
project Local Editable but does not sync on merge. Sandbox Identity panel with a link to the parent. Delete button hidden (sandboxes are deleted from the sandboxes UI).
credentials Editable Editable, will sync on merge.
collections Editable Editable, will sync on merge (handled in #4613).
webhook_security Inherited Auth methods are shared with the sandbox and enforced on its webhook triggers, but can only be created, edited, or deleted from the parent project.
collaboration Local Editable but does not sync on merge. Adds a parent admin floor rule (see below).
security (MFA) Inherited Read-only, value cloned from parent at provision time, MFA toggle disabled.
vcs Local Editable but does not sync on merge.
data-storage Local Editable but does not sync on merge.
history-exports Local Action button stays, banner explains scope.

A few things worth knowing about how this is implemented, because they affected the scope:

The merge layer is already protective. I started worrying that "Local" and "Inherited" were just UI promises and that a user changing data retention in a sandbox would actually push that value back to the parent on merge. After tracing through MergeProjects.merge_project/3 and Provisioner.import_document/4, that turns out not to be the case. The merge document only takes [:name, :description, :env, :color] from the target (not the source), and the provisioner only casts [:id, :name, :description] on the project itself. Project-level fields like requires_mfa, concurrency, retention_policy, history_retention_period, dataclip_retention_period aren't in the cast list, so they never propagate. Webhook auth methods, project_users, and the GitHub repo connection aren't in the merge document at all. I added regression tests in sandboxes_test.exs to lock this in so a future change to MergeProjects or the Provisioner doesn't accidentally start syncing them.

The parent admin floor rule is enforced server-side, not just in the UI. A user who is admin or owner on any ancestor project (walking the parent chain, so admin on grandparent counts) cannot be removed from a sandbox. The UI disables the Remove button with a tooltip explaining why, but Projects.delete_project_user!/1 also raises an ArgumentError if you try to do it directly, so a curl bypass or a future code path can't get around it. There's a unit test covering both paths.

Webhook auth methods stay enforced but can't be managed from the sandbox. Auth methods flow down from the parent and are still applied to the sandbox's webhook triggers, but the sandbox UI points users back to the parent for create/edit/delete. The tab is still present so users can find it; the panel renders a notice and a link to the parent project's webhook security tab instead of the full management table.

Closes #3398

Validation steps

Setup

  1. Create a project called Cat Shelter Registry.
  2. In Project Settings, set MFA to required on the Security tab and change History Retention to 7 days under Data Storage. We'll use these later to confirm they don't get overwritten.
  3. On the Collaboration tab, add a teammate as admin on this project.
  4. Go to Sandboxes and create a sandbox called sandbox-test. Open it.

You're now inside the sandbox. Every test below happens from here unless I say otherwise.

1. Sandbox Setup tab (Local)

Navigate to Project Settings in the sandbox. The first tab should be Setup.

What you should see:

  • A blue banner at the top: "Changes you make here only apply to this sandbox and do not sync to the parent project on merge."
  • The section title says Sandbox setup
  • The first card is Sandbox Identity
  • Below the description, a line: "Identifies this sandbox within its parent: [Cat Shelter Registry]" with the parent name as a link
  • The Export project button is still there
  • The danger zone at the bottom reads "The danger zone" with a Delete sandbox button. Clicking it opens a confirmation modal that requires typing the sandbox name; submitting calls Sandboxes.delete_sandbox/2 and redirects to the root project's workflows page with the flash "Sandbox and all its associated descendants deleted"

2. Credentials and Collections (Editable)

These are the two tabs where changes flow back. We'll create one of each in the sandbox now and confirm in step 7 that they show up in the parent after merge.

Credentials

  1. Click the Credentials tab. You should see a green banner: "Changes you make here will sync to the parent project on merge."
  2. Click New credential, pick any type (raw JSON is quickest), and create a credential called sandbox-only-credential.
  3. In another browser tab, open the parent project (Cat Shelter Registry) → Credentials. Confirm sandbox-only-credential is not there yet — it lives only in the sandbox until merge.

Collections

  1. Click the Collections tab. Same green banner.
  2. Click New collection, name it sandbox-only-collection, and save.
  3. In the parent project's Collections tab, confirm sandbox-only-collection is not there yet.

3. Webhook Security (managed in parent)

Click Webhook Security. Instead of the usual auth methods table, you should see a notice that reads something like:

Webhook authentication is managed in the parent project. Methods are shared with this sandbox and enforced on its webhook triggers, but can only be created, edited, or deleted from the parent.

Followed by a link: "Manage them in the parent project ([Cat Shelter Registry])" pointing back to the parent's webhook security tab.

4. Collaboration and the parent admin floor rule

Click Collaboration. You should see a Local banner.

The collaborators table shows the current sandbox members. The teammate you added as admin in step 3 of Setup should also be listed (they were inherited when the sandbox was provisioned).

Now hover over the Remove Collaborator button next to that admin. The button should be disabled, and the tooltip should read:

Cannot remove a user who is admin or owner on the parent project

For comparison, add another teammate to the sandbox as editor (not admin on the parent). Their Remove button should be enabled normally.

5. Security tab (Inherited)

Click Security. You should see:

  • A yellow banner: "These settings are inherited from the parent project ([Cat Shelter Registry]) and cannot be changed here."
  • The MFA toggle is visible but disabled (cursor shows not-allowed on hover, the switch reflects the parent's value)
  • Clicking the toggle does nothing

The value you see should match what you set on the parent in step 2 of Setup.

6. The other Local tabs

Click through Sync to GitHub, Data Storage, History Exports. Each should show the blue Local banner at the top. The forms remain editable. This is intentional: you can change these in the sandbox to support sandbox-specific workflows, but the values do not sync back.

7. Merge: what flows back, what doesn't

This is the part that worried me when I started, so worth confirming hands-on.

Still in the sandbox:

  1. Go to Data Storage and change History Retention to 90 days (or anything different from the parent's 7).
  2. Go to Setup and change Project Concurrency to 1.
  3. Open the parent project's settings briefly and note the starting values — History Retention 7 days, MFA required, no concurrency override.
  4. From the Sandboxes page, merge the sandbox into the parent.
  5. Open the parent project (Cat Shelter Registry) and walk through each tab.

Credentials (Editable — should sync):

  • sandbox-only-credential should now appear in the parent's Credentials tab.

Collections (Editable — should sync):

Setup (Local — should not sync):

  • Project name and description unchanged.
  • Concurrency unchanged (didn't become 1).

Data Storage (Local — should not sync):

  • History Retention is still 7 days (didn't change to 90).
  • I/O retention / policy unchanged.

Security (Inherited — should not sync):

  • MFA is still required (didn't get unset).

Editable categories flow back, Local and Inherited categories don't. That asymmetry is what sandboxes_test.exs locks in at the merge-document/provisioner level.

AI Usage

  • I have used Claude Code
  • I have used another model
  • I have not used AI

Pre-submission checklist

  • I have performed an AI review of my code (we recommend using /review with Claude Code)
  • I have implemented and tested all related authorization policies.
  • I have updated the changelog.
  • I have ticked a box in "AI usage" in this PR

@github-project-automation github-project-automation Bot moved this to New Issues in Core Apr 17, 2026
@github-actions
Copy link
Copy Markdown

Security Review

⚠️ Automated security review did not complete.

Claude hit the max-turns limit or encountered an error before posting findings.
A manual review of S0 (project-scoped data access), S1 (authorization policies),
and S2 (audit trail coverage) is recommended for this PR.

See the workflow run for details.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.85%. Comparing base (1a7478f) to head (bd9d16c).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4632      +/-   ##
==========================================
+ Coverage   89.75%   89.85%   +0.09%     
==========================================
  Files         444      445       +1     
  Lines       21661    21727      +66     
==========================================
+ Hits        19442    19522      +80     
+ Misses       2219     2205      -14     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Collaborator

@josephjclark josephjclark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fantastic!

It feels a lot better to think about this stuff here than in the issue. The top banner is working great - setting per-group context really helps

Some minor tweaks in comments and maybe we'll want to tune the UI right at the end

Comment thread lib/lightning_web/components/sandbox_settings_banner.ex Outdated
Comment thread lib/lightning_web/live/project_live/settings.html.heex Outdated
Comment thread lib/lightning_web/live/project_live/settings.html.heex Outdated
Comment thread lib/lightning_web/components/sandbox_settings_banner.ex Outdated
@elias-ba
Copy link
Copy Markdown
Contributor Author

elias-ba commented Apr 19, 2026

@josephjclark ready for another look. Addressed the five inline comments. One wording call to flag: you'd suggested "Webhook security is disabled for sandboxes", but auth methods from the parent are still enforced on the sandbox's webhook triggers, so I went with "Webhook authentication is managed in the parent project" instead - happy to swap if you prefer yours.

@elias-ba elias-ba requested a review from josephjclark April 19, 2026 23:15
@elias-ba
Copy link
Copy Markdown
Contributor Author

elias-ba commented Apr 20, 2026

@josephjclark just a bit of nudge on this one too whenever you have 5 mins to QA. Also feel free to merge it in the Sandbox Collection branch

@josephjclark
Copy link
Copy Markdown
Collaborator

Yep thanks @elias-ba - I'm at the end of my day now but I'll likely get this merged in the morning

Base automatically changed from sandbox-collections to main April 21, 2026 03:56
@josephjclark
Copy link
Copy Markdown
Collaborator

@elias-ba good catch on the auth thing - I've opened up #4643 to deal with this. For now let's keep your wording.

Make Project Settings behave differently inside a sandbox, per the
categorization agreed in #3398:

- Each tab shows a banner (via `SandboxSettingsBanner`) stating whether
  changes flow on merge: Local, Editable, or Inherited.
- Sandbox Setup tab: identity card links back to the parent project;
  the danger zone's button is "Delete sandbox" and routes through
  `Sandboxes.delete_sandbox/2` with a name-typing confirm modal, landing
  on the root project's workflows page after delete.
- Security tab: MFA toggle is read-only (value inherited from parent).
- Webhook security tab: auth methods are managed from the parent
  project; the sandbox tab shows a notice + link back.
- Credentials / Collections tabs: editable, flow on merge.
- Collaboration tab: new parent-admin floor rule — a user who is admin
  or owner on any ancestor project cannot be removed from a sandbox
  (enforced in UI via disabled Remove button and server-side via
  `Projects.delete_project_user!/1` raising).
- `root_project` assign added to the settings LV so the delete flow can
  redirect to the correct top-level ancestor.

Also a small UX fix on the shared sandbox delete confirm modal: dropped
the duplicate `<.errors>` row since the shared `<.input>` already
renders field errors.

Closes #3398.
@elias-ba elias-ba force-pushed the sandbox-settings-page branch from c57fe73 to 3c34961 Compare April 21, 2026 11:50
@elias-ba elias-ba requested a review from midigofrank April 21, 2026 12:00
@josephjclark
Copy link
Copy Markdown
Collaborator

Ack sorry I had a commit with some tiny style changes which failed to push earlier. I've just pushed it up now

Copy link
Copy Markdown
Collaborator

@midigofrank midigofrank left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nicely done

@elias-ba elias-ba merged commit af4c9f7 into main Apr 23, 2026
7 checks passed
@elias-ba elias-ba deleted the sandbox-settings-page branch April 23, 2026 07:23
@github-project-automation github-project-automation Bot moved this from New Issues to Done in Core Apr 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

sandboxes: Investigate Settings page

3 participants